Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.

...powered by www.netzwerkartist.de...

 <<   zurück
Visual Basic 2005 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual Basic 2005

Visual Basic 2005
1.233 S., mit 2 CDs, 59,90 Euro
Galileo Computing
ISBN 3-89842-585-1
gp Kapitel 6 Vererbung, Polymorphie und Schnittstellen
  gp 6.1 Basisklassen und abgeleitete Klassen
    gp 6.1.1 Die Ableitung einer Klasse
    gp 6.1.2 Klassen, die nicht vererben können
    gp 6.1.3 Zusammenfassung
  gp 6.2 Konstruktoren in abgeleiteten Klassen
    gp 6.2.1 Allgemeines
    gp 6.2.2 Die Konstruktoren der Klasse »GraphicCircle«
    gp 6.2.3 Der Zugriffsmodifizierer »Protected«
    gp 6.2.4 Konstruktorverkettung
    gp 6.2.5 Finalizer-Verkettung
    gp 6.2.6 Zusammenfassung
    gp 6.2.7 Der Stand des Projekts »CircleApplication«
  gp 6.3 Die Methoden in einer abgeleiteten Klasse
    gp 6.3.1 Geerbte Methoden mit »Shadows« verdecken
    gp 6.3.2 Überladen einer Basisklassenmethode
  gp 6.4 Ereignisse in der Vererbung
  gp 6.5 »Hat-eine«-Beziehungen (Aggregation)
    gp 6.5.1 Weiterleitung einer internen Objektreferenz
    gp 6.5.2 Verbergen des internen Objekts
    gp 6.5.3 Innere Klassen
  gp 6.6 Typumwandlung von Objektvariablen
    gp 6.6.1 Die implizite Typumwandlung von Objektreferenzen
    gp 6.6.2 Die explizite Typumwandlung von Objektreferenzen
    gp 6.6.3 Zusammenfassung
  gp 6.7 Abstrakte Klassen und Methoden
    gp 6.7.1 Problembeschreibung
    gp 6.7.2 Abstrakte Definitionen
  gp 6.8 Polymorphie
    gp 6.8.1 Überschreibbare Methoden mit »Overridable«
    gp 6.8.2 Inhomogene Mengen
    gp 6.8.3 Verdecken und Überschreiben geerbter Methoden
    gp 6.8.4 Überschreiben der Methode »ToString()« der Klasse »Object«
    gp 6.8.5 Nicht überschreibbare Methoden
    gp 6.8.6 Die Referenzen »Me«, »MyBase« und »MyClass«
    gp 6.8.7 Zusammenfassung
  gp 6.9 Erweiterung der Klassenhierarchie »CircleApplication«
    gp 6.9.1 Die Klasse »GeometricObject«
  gp 6.10 Schnittstellen
    gp 6.10.1 Einführung in die Schnittstellen
    gp 6.10.2 Schnittstellendefinition
    gp 6.10.3 Schnittstellenimplementierung
    gp 6.10.4 Abstrakte Klassen vs. Schnittstellen
    gp 6.10.5 Zusammenfassung


Galileo Computing

6.9 Erweiterung der Klassenhierarchie »CircleApplication«  downtop

Wir wollen uns nun noch einmal dem von uns immer weiter entwickelten Beispielprojekt CircleApplication zuwenden, den Entwurf um zwei weitere Klassen, nämlich um Rectangle und GraphicRectangle, ergänzen und uns dabei die in diesem Kapitel gewonnenen Kenntnisse zunutze machen. Die Klasse Rectangle soll ein Rechteck beschreiben und die Klasse GraphicRectangle eine Operation bereitstellen, um ein Rectangle-Objekt in einer grafikfähigen Komponente darzustellen – analog zur Klasse GraphicCircle.

Ebenso wie ein Circle-Objekt soll auch ein Rectangle-Objekt seine Lage beschreiben. Dazu wird ein Punkt vom Typ Point mit seinen x- und y-Koordinaten definiert. Um bei der üblichen Konvention grafischer Benutzeroberflächen zu bleiben, soll es sich dabei um den oberen linken Punkt des Rechtecks handeln. Die Größe eines Rechtecks wird durch seine Breite und Länge definiert. Außerdem sind Methoden vorzusehen, um Umfang und Fläche zu berechnen und zwei Rectangle-Objekte zu vergleichen.

Die Klassendefinition Rectangle könnte dann wie folgt aussehen:


' ---------- Delegate ----------
Public Delegate Sub MeasureErrorRectangleEventHandler(ByVal rect _
As Rectangle)
Public Class Rectangle
Implements IDisposable
' ---------- Ereignisse ----------
Public Event MeasureError As MeasureErrorRectangleEventHandler
' ---------- statische Felder ----------
Protected Shared intCountRectangles As Integer
' ---------- Felder ----------
Protected ptPosition As New Point
Protected dblBreite As Double = 0
Protected dblLaenge As Double = 0
Private bolCounterReduced As Boolean = False
' ---------- Konstruktoren ----------
Public Sub New()
intCountRectangles += 1
End Sub
Public Sub New(ByVal laenge As Double, ByVal breite As Double)
Me.New()
Me.breite = breite
Me.laenge = laenge
End Sub
Public Sub New(ByVal x As Integer, ByVal y As Integer, _
ByVal laenge As Double, ByVal breite As Double)
Me.New(laenge, breite)
Me.ptPosition.X = x
Me.ptPosition.Y = y
Me.Breite = breite
Me.Laenge = laenge
End Sub
Public Sub New(ByVal laenge As Double, _
ByVal breite As Double, ByVal pt As Point)
Me.new(Laenge, Breite)
Me.position = pt
End Sub
' --------- Finalizer --------------------
Protected Overrides Sub Finalize()
If Not bolCounterReduced Then
intCountRectangles -= 1
End If
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
intCountRectangles -= 1
bolCounterReduced = True
End Sub
' ---------- Eigenschaften ----------
Public Property Breite() As Double
Get
Return Me.dblBreite
End Get
Set(ByVal value As Double)
If (value >= 0) Then
Me.dblBreite = value
Else
RaiseEvent MeasureError(Me)
End If
End Set
End Property
Public Property Laenge() As Double
Get
Return Me.dblLaenge
End Get
Set(ByVal value As Double)
If (value >= 0) Then
Me.dblLaenge = value
Else
RaiseEvent MeasureError(Me)
End If
End Set
End Property
Public Property Position() As Point
Get
Return Me.ptPosition
End Get
Set(ByVal value As Point)
Me.ptPosition = value
End Set
End Property
' ---------- Instanzmethoden ----------
Public Function GetFlaeche() As Double
Return Me.dblLaenge * Me.dblBreite
End Function
Public Function GetUmfang() As Double
Return 2 * (Me.dblLaenge + Me.dblBreite)
End Function
Public Function Bigger(ByVal rect As Rectangle) _
As Rectangle
If (Me.dblLaenge * Me.dblBreite >= _
rect.dblLaenge * rect.dblBreite) Then
Return Me
End If
Return rect
End Function
Public Function Bigger(ByVal rect As Rectangle, _
ByRef equal As Boolean) As Rectangle
equal = False
If (Me.dblLaenge * Me.dblBreite = _
rect.dblLaenge * rect.dblBreite) Then
equal = True
End If
Return Me.Bigger(rect)
End Function
Public Sub MoveXY(ByVal newCenterPoint As Point)
Me.ptPosition = newCenterPoint
End Sub
' ---------- Klassenmethoden -----------
Public Shared Function GetFlaeche(ByVal laenge As Double, _
ByVal breite As Double) As Double
Return laenge * breite
End Function
Public Shared Function GetUmfang(ByVal laenge As Double, _
ByVal breite As Double) As Double
Return 2 * (laenge + breite)
End Function
Public Shared Function Bigger(ByVal rect1 As Rectangle, _
ByVal rect2 As Rectangle) As Rectangle
If (rect1.Breite * rect1.Laenge >= _
rect2.Laenge * rect2.Breite) Then
Return rect1
End If
Return rect2
End Function
Public Shared Function Bigger(ByVal rect1 As Rectangle, _
ByVal rect2 As Rectangle, ByVal equal As Boolean) As Rectangle
equal = False
If (rect1.Breite * rect1.Laenge = _
rect2.Laenge * rect2.Breite) Then
equal = True
End If
Return Rectangle.Bigger(rect1, rect2)
End Function
Public Shared Function IsBigger(ByVal rect1 As Rectangle, _
ByVal rect2 As Rectangle) As Boolean
If (rect1.breite * rect1.laenge >= _
rect2.laenge * rect2.breite) Then
Return True
End If
Return False
End Function
' ---------- Klasseneigenschaften ----------
Public Shared ReadOnly Property CountRectangles() As Integer
Get
Return Rectangle.intCountRectangles
End Get
End Property
End Class

Und nun auch noch der Code der Klasse GraphicRectangle:


Public Class GraphicRectangle
Inherits Rectangle
' Konstruktoren
Public Sub New()
MyBase.New()
End Sub
Public Sub New(ByVal laenge As Double, ByVal breite As Double)
MyBase.New(laenge, breite)
End Sub
Public Sub New(ByVal x As Integer, ByVal y As Integer, _
ByVal laenge As Double, ByVal breite As Double)
MyBase.New(x, y, laenge, breite)
End Sub
Public Sub New(ByVal laenge As Double, _
ByVal breite As Double, ByVal pt As Point)
MyBase.New(laenge, breite, pt)
End Sub
' Typspezifische Methode
Public Sub Draw()
Console.WriteLine("Das Rechteck wird gezeichnet")
End Sub
End Class

Es ist deutlich zu erkennen, dass sich die Klassen Rectangle und Circle ähneln, ebenso die beiden Klassen GraphicCircle und GraphicRectangle. Dies spricht dafür, allen vier Klassen eine Basisklasse vorzuschalten, welche die gemeinsamen Merkmale eines Kreises und eines Rechtecks beschreibt: Wir werden diese Klasse im Folgenden GeometricObject nennen.

Ein weiteres Argument für diese Lösung ist die sich daraus ergebende Gleichnamigkeit der gemeinsamen Merkmale: Es werden dann die Methoden, die ihren Fähigkeiten nach Gleiches leisten, unabhängig vom Typ des zugrunde liegenden Objekts in gleicher Weise aufgerufen. Einerseits lässt sich dadurch die abstrahierte Artverwandtschaft der beiden geometrischen Objekte Kreis und Rechteck verdeutlichen, andererseits wird die Benutzung der Klassen wesentlich vereinfacht, da nicht zwei unterschiedliche Methodennamen dasselbe Leistungsmerkmal beschreiben.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 6.6     Die Klassenhierarchie des Projekts »CircleApplication«

Nach diesen ersten Überlegungen soll nun die Klasse GeometricObject implementiert werden.


Galileo Computing

6.9.1 Die Klasse »GeometricObject«  toptop

Vergleichen wir jetzt Schritt für Schritt die einzelnen Klassenmitglieder von Circle und Rectangle, um daraus ein einheitliches Konzept für den Entwurf des Oberbegriffs GeometricObject zu formulieren.

Instanzvariablen und Eigenschaftsmethoden

Ein Circle-Objekt wird nach dem augenblicklichen Stand durch seinen Radius und ein Point-Objekt namens center beschrieben, dessen Koordinaten über die Eigenschaftsmethoden XKoordinate und YKoordinate festgelegt werden.

Auch zur Positionsbeschreibung eines Rechtecks dient ein Point-Objekt. Allerdings kann nach der augenblicklichen Implementierung die Positionierung nur über die direkte Übergabe eines Point-Objekts an die Eigenschaft Position erfolgen.

Obwohl sich beide Klassendefinitionen hinsichtlich der Positionsdatenübergabe unterscheiden, bietet es sich uns an, jeden der beiden Typen sowohl mit einer Eigenschaft vom Typ Point als auch mit Eigenschaften, die das Festlegen der Koordinaten in X- und in Y-Richtung ermöglichen, auszustatten. Mit dieser ergänzenden Maßnahme erhöhen wir für jeden Typ sogar noch die Flexibilität, ohne dabei Einbußen in Kauf nehmen zu müssen. Wir können also alle mit der Positionierung im Zusammenhang stehenden Klassenelemente in die Basisklasse GeometricObject auslagern.

Der Radius eines Circle-Objekts sowie die Länge und die Breite eines Rectangle-Objekts sind objektspezifische Daten. Diese Instanzvariablen lassen wir in den ursprünglichen Klassendefinitionen.

Beide Klassendefinitionen definieren außerdem das private Feld counterReduced, mit dem der Objektzähler beim Zerstören des Objekts mit Dispose und dem Destruktor gesteuert wird. Unter Änderung des Zugriffsmodifizierers in Protected ist auch dieses Datenmember ein erstklassiger Kandidat für die neu geschaffene Basisklasse.


' ---------- Instanzvariablen in GeometricObject ----------
Protected center As New Point
Protected bolCounterReduced As Boolean = False
' ---------- Eigenschaftsmethoden ----------
Public Property XKoordinate() As Integer
Get
Return center.X
End Get
Set(ByVal Value As Integer)
center.X = Value
End Set
End Property
Public Property YKoordinate() As Integer
Get
Return center.Y
End Get
Set(ByVal Value As Integer)
´ center.Y = Value
End Set
End Property
Public Property Position() As Point
Get
Return Me.center
End Get
Set(ByVal value As Point)
Me.center = value
End Set
End Property

Konstruktoren und Destruktoren

Da sich Konstruktoren und Destruktoren nicht an die abgeleiteten Klassen vererben, bleiben die Erstellungs- und Zerstörungsroutinen in Circle und Rectangle unverändert. Ein eigener Konstruktor in GeometricObject ist nicht notwendig, weder der parameterlose noch ein parametrisierter.

Die Instanzmethoden

Ein Vergleich hinsichtlich der Instanzmethoden beider Klassen führt zu der Erkenntnis, dass beide Klassen eine gleichnamige überladene Methode Bigger veröffentlichen, die zwei Objekte miteinander vergleicht und die Referenz auf das größere der verglichenen Objekte als Resultat des Methodenaufrufs an den Benutzer zurückgibt. Aus logischer Sicht erbringen diese Methoden sowohl in Circle als auch in Rectangle dieselbe Leistung, unterscheiden sich nur im Typ des Parameters bzw. des Rückgabewerts: Die Bigger-Methode in der Circle-Klasse nimmt die Referenz auf ein Kreisobjekt entgegen, die Klasse Rectangle die Referenz auf ein Rectangle-Objekt.

Wir können uns den Umstand zunutze machen, dass sowohl die Circle- als auch die Rectangle-Klasse aus derselben Basisklasse abgeleitet werden, und müssen dazu nur den Typ des Parameters und der Rückgabe entsprechend anpassen:


' ----- Instanzmethoden -----
Public Function Bigger(ByVal obj As GeometricObject) _
As GeometricObject
If Me.GetFlaeche >= obj.GetFlaeche Then
Return Me
End If
Return obj
End Function
Public Function Bigger(ByVal obj As GeometricObject, _
ByRef equal As Boolean) As GeometricObject
equal = False
If Me.GetFlaeche = obj.GetFlaeche Then
equal = True
End If
Return Me.Bigger(obj)
End Function

Weil Circle und Rectangle von GeometricObject abgeleitet werden, gilt, dass sowohl ein Circle-Objekt als auch Rectangle-Objekt gleichzeitig auch ein Objekt vom Typ GeometricObject ist. Beide Methoden werden unter Übergabe einer spezifischen Objektreferenz aufgerufen, welche die Laufzeitumgebung implizit konvertiert. Als Nebeneffekt beschert uns diese Verallgemeinerung, dass wir nun in der Lage sind, die Flächen von zwei verschiedenen Typen zu vergleichen, denn nun kann die Bigger-Methode auf eine Circle-Referenz aufgerufen und als Argument die Referenz auf ein Rectangle-Objekt übergeben werden.

In der Bigger-Methode wird GetFlaeche aufgerufen, die natürlich weiterhin typgebunden ist, da die Fläche auf die typspezifischen Daten Radius bzw. Laenge und Breite zugreift. Es liegt aber nahe, GetFlaeche als abstrakte Methode festzulegen, die von den ableitenden Klassen Circle und Rectangle überschrieben werden muss. Polymorph wird dann der Zugriff auf die richtige Implementierung sichergestellt. Mit derselben Argumentation kann auch GetUmfang als abstrakte Methode in GeometricObject definiert werden.


' ---------- abstrakte Instanzmethoden ---------------
Public MustOverride Function GetFlaeche() As Double
Public MustOverride Function GetUmfang() As Double

MoveXY ist im Vergleich zu den beiden vorgenannten Methoden typunabhängig und kann daher vollständig in GeometricObject implementiert werden.


Public Sub MoveXY(ByVal newCenterPoint As Point)
Me.center = newCenterPoint
End Sub

Die Klassenmethoden

Die Argumentation, die uns dazu brachte, die Instanzmethode Bigger in der Basisklasse zu codieren, kann auch bei den Klassenmethoden Bigger und IsBigger geführt werden. Wir müssen jeweils nur den Typ des Parameters und bei der Methode Bigger auch den des Rückgabewerts ändern, um die Klassenmethoden in der Klasse GeometricObject bereitzustellen.


' ---------- Klassenmethoden in GeometricObject -----------
Public Shared Function Bigger(ByVal obj1 As GeometricObject, _
ByVal obj2 As GeometricObject) As GeometricObject
If (obj1.GetFlaeche() >= obj2.GetFlaeche()) Then
Return obj1
End If
Return obj2
End Function
Public Shared Function Bigger(ByVal obj1 As GeometricObject, _
ByVal obj2 As GeometricObject, ByRef equal As Boolean) _
As GeometricObject
equal = False
If (obj1.GetFlaeche() = obj2.GetFlaeche()) Then
equal = True
End If
Return GeometricObject.Bigger(obj1, obj2)
End Function
Public Shared Function IsBigger(ByVal obj1 As GeometricObject, _
ByVal obj2 As GeometricObject) As Boolean
If (obj1.GetFlaeche() >= obj1.GetFlaeche()) Then
Return True
End If
Return False
End Function

Das Ereignis »MeasureError«

Wir wollen nun auch das Ereignis MearureError in GeometricObject implementieren und es allen abgeleiteten Klassen bereitstellen. Das ist jedoch nicht ganz so einfach und verlangt eine genauere Analyse, wie Sie noch sehen werden.

Rufen wir uns zuerst die Implementierung des Ereignisses in der Klasse Circle in Erinnerung:


Public Event MeasureError(ByVal k As Circle)

Ausgelöst wird das Ereignis MeasureError in der Eigenschaftsmethode Radius mit:


Public Property Radius() As Double
Get
Return dblRadius
End Get
Set(ByVal Value As Double)
If Value >= 0 Then
dblRadius = Value
Else
RaiseEvent MeasureError(Me)
End If
End Set
End Property

Der Übergabeparameter des Delegaten vom Typ Circle versetzt den Benutzer der Klasse in die Lage, bei einer unzulässigen Zuweisung an Radius im Ereignishandler auf die Objektreferenz den Radius neu einzugeben, beispielsweise:


Public Sub SetNewRadius(sender As Circle)
Console.WriteLine("Unzulässige Eingabe des Radius.")
Console.Write("Neueingabe: ")
sender.Radius = Console.ReadLine()
End Sub

Ähnlich ist der Sachverhalt in Rectangle. Diese Klasse stellt auch ein Ereignis MeasureError bereit, dieses ist jedoch vom Typ MeasureErrorRectangleEventHandler.

Das Ereignis wird, ähnlich wie in der Klasse Circle, ausgelöst, wenn einer der beiden Seitenlängen ein negativer Wert zugewiesen wird.

MeasureError soll in der Klasse GeometricObject als Ereignis dienen, wenn entweder einem Circle- oder einem Rectangle-Objekt ein unzulässiger Wert übergeben wird. Damit wird sofort die erste Konsequenz klar: Da wir weiterhin davon ausgehen, dass bei Ereignisauslösung in einem Circle-Objekt dieses die Referenz auf sich selbst an den Ereignishandler übergeben will, müssen wir den Typ in der Parameterliste des Delegaten entsprechend anpassen. Ein Rectangle-Objekt, dessen Ereignis bisher noch nicht die Referenz auf sich selbst dem Ereignishandler mitteilte, kann davon nur profitieren.


' ---------- vorläufiger Delegat in GeometricObject --------
Public Delegate Sub MeasureErrorEventHandler
´ (ByVal geo As GeometricObject)

Wir lassen zunächst alle anderen Aspekte hinsichtlich der Implementierung in den Klassen außen vor und stellen weitere Überlegungen an. Wird MeasureError bei dieser Definition in einem Circle-Objekt ausgelöst, kann im Ereignishandler der Radius nach vorheriger Konvertierung der Referenz sender neu zugewiesen werden, z.  B.:


Public Sub SetNewMeasure(ByVal sender As GeometricObject)
...
sender.Radius = Console.ReadLine()
End Sub

Etwas anders verhält es sich jedoch, wenn entweder die Breite oder die Länge eines Rechtecks nicht den Vorgaben entspricht. Woher soll der Ereignishandler die Information nehmen, welcher Eigenschaft ein unzulässiger, negativer Wert zugewiesen worden ist? Es stehen keinerlei Informationen darüber zur Verfügung, ob es sich um Breite oder Laenge gehandelt hat.

Wir sollten eine Lösung für dieses Problem finden, denn dasselbe Ereignis bei der Auslösung durch ein Circle-Objekt anders zu behandeln als bei der Auslösung durch ein Rectangle-Objekt wäre eine schlechte Alternative. Die Lösung muss für beide in Frage kommenden Typen zu einer gleichwertigen Behandlung führen.

Der Ansatz, der uns zum Ziel führt, geht über die Definition eines weiteren Parameters, der im Ereignishandler die Referenz auf den Wert liefert, der fälschlicherweise zugewiesen wurde.


Public Delegate Sub MeasureErrorEventHandler
(ByVal geo As GeometricObject, ByRef measure As Double)

Der Ereignishandler in einer Clientklasse, der die Korrektur durch den Anwender zur Laufzeit zulässt, könnte im einfachsten Fall wie folgt codiert werden:


' Ereignishandler in der Clientklasse
Public Sub SetNewMeasure(
ByVal sender As GeometricObject, ByRef measure As Double)
measure = Console.ReadLine()
End Sub

Sollte es sich als notwendig erweisen, kann die Referenz sender daraufhin untersucht werden, ob der Auslöser ein Circle- oder ein Rectangle-Objekt war. Aber auch ohne diese Unterscheidung wird der Referenzparameter den neuen Wert immer richtig an die Ereignisquelle weiterleiten.

Wir haben das endgültige Ziel nahezu erreicht, müssen aber jetzt noch dem Umstand Beachtung schenken, dass Ereignisse in der Klasse ausgelöst werden, in der sie definiert sind. Damit das in einer Basisklasse definierte Ereignis auch für den Code in einer abgeleiteten Klasse zugänglich ist, muss die Basisklasse GeometricObject nicht nur das Ereignis MeasureError, sondern auch eine Methode bereitstellen, die in der Art eines Adapters die Verbindung zwischen einer Sub- und der Basisklasse sicherstellt, wenn die abgeleitete Klasse das Ereignis auslöst:


Public Sub OnMeasureError(ByVal obj As GeometricObject, _
ByVal measure As Double)
RaiseEvent MeasureError(obj, measure)
End Sub

Objekte der abgeleiteten Klassen Circle und Rectangle müssen jetzt die Methode OnMeasureError aufrufen und neben dem Me-Zeiger auch den Wert als Referenz übergeben, der die Ablehnung im Objekt verursacht hat. Behandelt der Client das Ereignis und weist einen neuen Wert zu, wird über OnMeasureError der korrigierte Wert an das Objekt der abgeleiteten Klasse weitergeleitet. Hier kann nun eine erneute Überprüfung durch einen weiteren Aufruf der entsprechenden Ereignismethode erfolgen. Beispielhaft sei das an der Eigenschaft Radius in der Klasse Circle gezeigt:


Public Property Radius() As Double
Get
Return dblRadius
End Get
Set(ByVal Value As Double)
If Value >= 0 Then
dblRadius = Value
Else
Me.OnMeasureError(Me, Value)
End If
End Set
End Property


Anmerkung

Sie finden den vollständigen Code zu diesem Beispiel auf der Buch-CD unter ...\Kapitel 6\CircleApplication_5.


 <<   zurück
  
  Zum Katalog
Zum Katalog: Visual Basic 2005
Visual Basic 2005
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Visual C# 2005






 Visual C# 2005


Zum Katalog: Fortgeschrittene Programmierung mit Visual C# 2005






 Fortgeschrittene
 Programmierung
 mit Visual C# 2005


Zum Katalog: Das Programmierhandbuch SQL Server 2005






 Das Programmier-
 handbuch
 SQL Server 2005


Zum Katalog: Einstieg in Visual Basic 2005






 Einstieg in
 Visual Basic 2005


Zum Katalog: Einstieg in Visual C# 2005






 Einstieg in
 Visual C# 2005


Zum Katalog: Konzepte und Lösungen für Microsoft-Netzwerke






 Konzepte und
 Lösungen für
 Microsoft-Netzwerke


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo








Copyright © Galileo Press 2007
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de